using System;
using System.Configuration;
using System.IO;

namespace Seaskyer.WebApp.Utility.StrongUpload
{
	/// <summary>
	/// Provides information about a file that has been uploaded.
	/// </summary>
	/// <remarks>
	/// All files are streamed to disk as they arrive. Each UploadedFile instance contains
	/// information about a single uploaded file, as well as methods that manipulate it.
	/// The <see cref="UploadedFileCollection"/> provides a collection of all of the files
	/// uploaded in a given request.
	/// </remarks>
	public sealed class UploadedFile
	{
		string _clientName;
		string _clientPath;
		string _serverPath;
		string _contentType;
		string _sourceElement;
		long _contentLength;

		Stream _intermediateStream;
		byte[] _intermediateBuffer = null;

		#region Properties
		/// <summary>
		/// Gets the file name of the uploaded file as it was on the client machine.
		/// </summary>
		public string ClientName
		{
			get
			{
				return _clientName;
			}
		}

		/// <summary>
		/// Gets the entire path of where the uploaded file was located on the client machine.
		/// </summary>
		public string ClientPath
		{
			get
			{
				return _clientPath;
			}
		}

		/// <summary>
		/// Gets the path to the temporary file on the server in which the uploaded file data is stored.
		/// </summary>
		public string ServerPath
		{
			get
			{
				return _serverPath;
			}
		}

		/// <summary>
		/// Gets the MIME content type of the uploaded file.
		/// </summary>
		public string ContentType
		{
			get
			{
				return _contentType;
			}
		}

		/// <summary>
		/// Gets the name of the source form input element that the upload came from.
		/// </summary>
		public string SourceElement
		{
			get
			{
				return _sourceElement;
			}
		}

		/// <summary>
		/// Gets the length (in bytes) of the uploaded file.
		/// </summary>
		public long ContentLength
		{
			get
			{
				return _contentLength;
			}
		}

		/// <summary>
		/// Returns the intermediate storage stream for an upload.
		/// </summary>
		public Stream IntermediateStream
		{
			get
			{
				if (GetIsMemoryUpload())
				{
					if (_intermediateBuffer == null)
					{
						if (_intermediateStream == null || !_intermediateStream.CanWrite)
							_intermediateStream = new MemoryStream();

						return _intermediateStream;
					}
					else
					{
						_intermediateStream = null;

						return new MemoryStream(_intermediateBuffer);
					}
				}
				else
				{
					return File.Create(ServerPath);
					//throw new IOException("IntermediateStream is only valid for a memory upload.");
				}
			}
		}
		#endregion

		internal UploadedFile(string clientPath, string contentType, string sourceElement)
		{
			_serverPath = GetTempFileName();
			_clientPath = clientPath;
			_contentType = contentType;
			_sourceElement = sourceElement;

			// TODO: ensure this is cross-platform compatible
			_clientName = Path.GetFileName(_clientPath);
		}

		/// <summary>
		/// Saves the uploaded file to a given path on the server, then deletes the temporary file.
		/// </summary>
		/// <param name="path">The path to which to save the file.</param>
		public void SaveAs(string path)
		{
			SaveAs(path, false);
		}

		/// <summary>
		/// Saves the uploaded file to a given path on the server, then deletes the temporary file.
		/// A boolean passed in determines whether to silently overwrite the destination file.
		/// </summary>
		/// <param name="path">The path to which to save the file.</param>
		/// <param name="overwrite">A boolean determining whether to overwrite the file silently if it exists.</param>
		public void SaveAs(string path, bool overwrite)
		{
			if (overwrite)
				File.Delete(path);

			if (GetIsMemoryUpload())
			{
				FileStream s = File.Create(path);

				if (_intermediateBuffer != null)
				{
					s.Write(_intermediateBuffer, 0, _intermediateBuffer.Length);

					_intermediateBuffer = null;
					_intermediateStream = null;
				}

				s.Close();
			}
			else
			{
				File.Move(_serverPath, path);
			}
		}

		internal bool GetIsMemoryUpload()
		{
			if (ConfigurationSettings.AppSettings["storageProvider"] != null)
			{
				switch (ConfigurationSettings.AppSettings["storageProvider"].ToLower())
				{
					case "memory":
						return true;
					default:
					case "file":
						return false;
				}
			}
			else
			{
				return false;
			}
		}

		internal void SetUploadComplete(long contentLength)
		{
			_contentLength = contentLength;

			if (GetIsMemoryUpload())
			{
				MemoryStream s = _intermediateStream as MemoryStream;
		
				_intermediateBuffer = new byte[contentLength];

				Buffer.BlockCopy(s.GetBuffer(), 0, _intermediateBuffer, 0, (int)_contentLength);
			}
		}

		internal static string GetTempFileName()
		{
			string uploadPath = ConfigurationSettings.AppSettings["uploadPath"];

			if (uploadPath == null || uploadPath.Length == 0)
				uploadPath = Path.GetTempPath();

			uploadPath = Path.Combine(uploadPath, Guid.NewGuid().ToString("B"));

			return uploadPath;
		}
	}
}